package com.xinyue.cross.server.client;

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.xinyue.cross.server.client.model.GameServerConnectionModel;
import com.xinyue.cross.server.client.model.NettyConnectionCloseEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;

import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;

public class GameServerBalanceService implements ApplicationListener<NettyConnectionCloseEvent> {
    private Logger logger = LoggerFactory.getLogger(GameServerBalanceService.class);
    @Autowired
    private GameServerConnectionService gameServerConnectionService;

    @Autowired
    private XinyueCrossClientConfig clientConfig;
    private Cache<String, GameServerConnectionModel> userConnectionCache;


    @PostConstruct
    public void init() {
        this.userConnectionCache = CacheBuilder.newBuilder().initialCapacity(clientConfig.getBalanceCacheMaxSize())
                .maximumSize(clientConfig.getBalanceCacheMaxSize())
                .expireAfterAccess(clientConfig.getBalanceExpireTimeSecond(), TimeUnit.SECONDS).build();
        logger.info("初始化负载均衡缓存成功，maxSize: {},expireTimeSecond:{}", clientConfig.getBalanceCacheMaxSize(), clientConfig.getBalanceExpireTimeSecond());
    }

    private String getCacheKey(String serviceId, String userKey) {
        return userKey + ":" + serviceId;
    }

    public GameServerConnectionModel selectNettyClientService(String serviceId, String userKey) throws NotFundGameServerException {
        if (userKey == null) {
            throw new IllegalArgumentException("选择服务器连接客户端时，userKey不能为空");
        }
        String cacheKey = this.getCacheKey(serviceId, userKey);
        GameServerConnectionModel gameServerConnectionModel = this.userConnectionCache.getIfPresent(cacheKey);
        if (gameServerConnectionModel != null) {
            logger.debug("缓存中存在对应的服务连接，直接返回");
            return gameServerConnectionModel;
        }
        gameServerConnectionModel = this.selectOneClient(serviceId, userKey);
        if (gameServerConnectionModel != null) {
            this.userConnectionCache.put(cacheKey, gameServerConnectionModel);
        }
        return gameServerConnectionModel;
    }

    private GameServerConnectionModel selectOneClient(String serviceId, String userKey) {
        Map<String, List<GameServerConnectionModel>> gameServerConnectionModelMap = gameServerConnectionService.getGameServerConnectionModelMap();
        List<GameServerConnectionModel> gameServerConnectionModelList = gameServerConnectionModelMap.get(serviceId);
        if (gameServerConnectionModelList == null || gameServerConnectionModelList.isEmpty()) {
            logger.info("不存在的服务，serviceId: {}", serviceId);
            return null;
        }
        int hashCode = Math.abs(userKey.hashCode());
        int index = hashCode % gameServerConnectionModelList.size();
        GameServerConnectionModel gameServerConnectionModel = gameServerConnectionModelList.get(index);
        logger.info("网关选择的服务连接，serviceId: {},instanceName: {}, serviceKey: {}", serviceId, gameServerConnectionModel.getInstanceName(), gameServerConnectionModel.getServiceKey());
        return gameServerConnectionModel;
    }

    @Override
    public void onApplicationEvent(NettyConnectionCloseEvent event) {
        List<String> removeUserKeyList = new ArrayList<>();
        ConcurrentMap<String, GameServerConnectionModel> concurrentMap = this.userConnectionCache.asMap();
        concurrentMap.forEach((userKey, model) -> {
            if (model.getNettyClientService().getChannel() == event.getChannel()) {
                removeUserKeyList.add(userKey);
            }
        });
        if (!removeUserKeyList.isEmpty()) {
            logger.info("服务关闭，移除用户缓存的服务,removeUserKeyList: {}", removeUserKeyList);
            this.userConnectionCache.invalidateAll(removeUserKeyList);
        }
    }
}
